package server;

import java.util.HashMap;
import java.util.ArrayList;
import java.util.Set;
import java.sql.SQLException;
import server.Log.directOpt;

/**
 * Mock MYSQLComm for testing
 */
class MYSQLCommMock extends MYSQLComm {

    ArrayList<Protocol> protocols = new ArrayList<Protocol>();
    ArrayList<String> protocolNames = new ArrayList<String>();
    ArrayList<Password> passwords = new ArrayList<Password>();
    ArrayList<String> rulesets = new ArrayList<String>();
    HashMap<String, ArrayList<Rule> > rules = 
        new HashMap<String, ArrayList<Rule> >();
	ArrayList<String> globalLog = new ArrayList<String>();
	ArrayList<String> logs = new ArrayList<String>();

    // Used for deleteRule/deleteRuleComplete
    String tableToDelete;
    int indexToDelete;

	int lastGetGlobalLogsDays = 0;	// last 'days' given to getGlobalLogs
	int	lastLogDays;
	Log.typeOpt lastLogType;
	String lastLogProtocol;
	String lastLogIp;
	Integer lastLogPort;
	directOpt lastLogDir;

    boolean failNext; // Fail the next action?

    public MYSQLCommMock() {
        reset();
    }

	// Not part of interface - used for testing
	public void updateIndices() {
		int i;
		for (ArrayList<Rule> lst : rules.values()) {
			for (i = 0; i < lst.size(); ++i) {
				lst.get(i).index = i + 1;
			}
		}
	}

    // flush everything
    public void reset() { 
        protocols.clear();
        passwords.clear();
        rules.clear();
        rulesets.clear();
		globalLog.clear();
		logs.clear();
        failNext = false;
		lastGetGlobalLogsDays = 0;
    }

    // Cause the next operation (whatever it is) to fail
    public void failNextRequest() {
        failNext = true;
    }

    // returns table name for ruleset + direction
    public String getTableName(String ruleset, boolean in) {
        if (ruleset.compareToIgnoreCase("Default") == 0) {
            return in ? "ActiveRules_In" : "ActiveRules_Out";
        }

        return in ? 
            "RuleSet_" + ruleset + "_In" :
            "RuleSet_" + ruleset + "_Out";
    }

	private Rule dupRule(Rule rule) {
		Rule result;
		if (rule.type == Rule.typeOpt.IP) {
			result = new Rule(rule.index, rule.iprange, rule.port, rule.permission);
		} else {
			result = new Rule(rule.index, rule.port, rule.protocol, rule.permission);
		}

		return result;
	}

	public void addLog(String log) {	// not part of MYSQLComm interface
		logs.add(log);
	}

    // add rule sanity check ?
    // check that it's numbering is ok ?
    public void addRule(String table, Rule rule) throws SQLException
    {
        if (failNext) {
            failRequest();
        }
        if (rules.get(table) == null) {
            throw new SQLException("MockSQL: Invalid table " + table);
        }
        try {
            rules.get(table).add(rule.index - 1, dupRule(rule));
        } catch (Exception e) {
            throw new SQLException(
                    "MockSQL: Index " + rule.index + " invalid for table " + table);
        }

		updateIndices();
    }

    public void deleteRule(String table, int index) throws SQLException
    {
        if (failNext) {
            failRequest();
        }
        tableToDelete = table;
        indexToDelete = index;
    }

    public void deleteRuleComplete(boolean complete) throws SQLException
    {
        if (failNext) {
            failRequest();
        }
        if (complete) {
            rules.get(tableToDelete).remove(indexToDelete - 1);
        }

        tableToDelete = null;
        indexToDelete = -1;

		updateIndices();
    }

    public Rule[] getRules(String table) throws SQLException
    {
        if (failNext) {
            failRequest();
        }

        return (Rule[])(rules.get(table).toArray(new Rule[rules.get(table).size()]));
    }

    public Password getPassword(String user) throws SQLException
    {
        if (failNext) {
            failRequest();
        }
        for (Password password : passwords) {
            if (password.user.equals(user)) {
                return password;
            }
        }

        throw new SQLException("No such user");
    }

    public void addProtocol(Protocol protocol) throws SQLException
    {
        if (failNext) {
            failRequest();
        }

        if (protocolNames.contains(protocol.protocol)) {
            throw new SQLException(
                    "MockSQL: Protocol to add already exists (" + 
                    protocol.protocol + ")");
        }

        protocols.add(protocol);
        protocolNames.add(protocol.protocol);
    }

    public void deleteProtocol(String name) throws SQLException
    {
        if (failNext) {
            failRequest();
        }
        for (int i = 0; i < protocols.size(); ++i) {
            if (protocols.get(i).protocol.equals(name)) {
                protocols.remove(i);
                protocolNames.remove(name);
                return;
            }
        }

        //throw new SQLException(
        //        "MockSQL: Asked to delete non existant protocol (" + name + ")");
    }

    public void updateProtocol(Protocol protocol) throws SQLException
    {
        if (failNext) {
            failRequest();
        }
        for (int i = 0; i < protocols.size(); ++i) {
            if (protocols.get(i).protocol.equals(protocol.protocol)) {
                protocols.remove(i);
                protocols.add(i, protocol);
                return;
            }
        }
        //throw new SQLException(
        //        "MockSQL: Asked to update non existant protocol (" + 
        //        protocol.protocol + ")");
    }

    public Protocol[] getProtocols() throws SQLException
    {
        if (failNext) {
            failRequest();
        }
        return (Protocol[])protocols.toArray(new Protocol[protocols.size()]);
    }

    public Protocol getProtocol(String name) throws SQLException
    {
        if (failNext) {
            failRequest();
        }
        for (Protocol protocol : protocols) {
            if (protocol.protocol.equals(name)) {
                return protocol;
            }
        }

        throw new SQLException(
                "MockSQL: Asked to get invalid protocol (" + name + ")");
    }

    public void createRuleSet(String name) throws SQLException
    {
        if (failNext) {
            failRequest();
        }
        if (rulesets.contains(name)) {
            throw new SQLException("MockSQL: Ruleset " + name + " already exists");
        }
        rules.put(getTableName(name, true), new ArrayList<Rule>());
        rules.put(getTableName(name, false), new ArrayList<Rule>());
        rulesets.add(name);
    }

    public void deleteRuleSet(String name) throws SQLException
    {
        if (failNext) {
            failRequest();
        }
        rules.remove(getTableName(name, true));
        rules.remove(getTableName(name, false));
        rulesets.remove(name);
    }

    public String[] getRuleSets() throws SQLException
    {
        if (failNext) {
            failRequest();
        }

        return (String[])rulesets.toArray(new String[rulesets.size()]);
    }

    public Log[] getLogs(Integer days, Log.typeOpt type, String protocol, String ip, Integer port, directOpt dir) throws SQLException
    {
        if (failNext) {
            failRequest();
        }

		lastLogDays = days;
		lastLogType = type;
		lastLogProtocol = protocol;
		lastLogIp = ip;
		lastLogPort = port;
		lastLogDir = dir;

        // TODO the best we can do here is verify we got the correct args
        return null;
    }

    public GlobalLog[] getGlobalLogs(int days) throws SQLException
    {
        if (failNext) {
            failRequest();
        }

		lastGetGlobalLogsDays = days;
		GlobalLog[] result = new GlobalLog[globalLog.size()];

		for (int i = 0; i < globalLog.size(); ++i) {
			result[i] = new GlobalLog(i, globalLog.get(i));
		}

        return result;
    }

    public void addGlobalLog(String error) throws SQLException
    {
        if (failNext) {
            failRequest();
        }

		globalLog.add(error);
    }

    private void failRequest() throws SQLException
    {
        failNext = false;
        throw new SQLException("MockSQL: Failing by request");
    }
}
